/*
 *  REDIR.C - redirection handling.
 *
 *
 *  History:
 *
 *    12/15/95 (Tim Norman)
 *        started.
 *
 *    12 Jul 98 (Hans B Pufal)
 *        Rewrote to make more efficient and to conform to new command.c
 *        and batch.c processing.
 *
 *    27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
 *        Added config.h include
 *
 *    22-Jan-1999 (Eric Kohl)
 *        Unicode safe!
 *        Added new error redirection "2>" and "2>>".
 *
 *    26-Jan-1999 (Eric Kohl)
 *        Added new error AND output redirection "&>" and "&>>".
 *
 *    24-Jun-2005 (Brandon Turner <turnerb7@msu.edu>)
 *        simple check to fix > and | bug with 'rem'
 */

#include "precomp.h"

#ifdef FEATURE_REDIRECTION

/*
 * CMD allows redirection of handles numbered 3-9 even though
 * these don't correspond to any STD_ constant.
 */
static const PCON_STREAM StdStreams[] = { StdIn, StdOut, StdErr };
static HANDLE ExtraHandles[10 - 3]; // 3 == ARRAYSIZE(StdStreams)

HANDLE GetHandle(UINT Number)
{
    if (Number < 3)
        return ConStreamGetOSHandle(StdStreams[Number]);
        // return GetStdHandle(STD_INPUT_HANDLE - Number);
    else if (Number < ARRAYSIZE(ExtraHandles) + 3)
        return ExtraHandles[Number - 3];
    else
        return INVALID_HANDLE_VALUE;
}

VOID SetHandle(UINT Number, HANDLE Handle)
{
    if (Number < 3)
    {
        ConStreamSetOSHandle(StdStreams[Number], Handle);
        /* Synchronize the associated Win32 handle */
        SetStdHandle(STD_INPUT_HANDLE - Number, Handle);
    }
    else if (Number < ARRAYSIZE(ExtraHandles) + 3)
        ExtraHandles[Number - 3] = Handle;
}

BOOL
PerformRedirection(REDIRECTION *RedirList)
{
    REDIRECTION *Redir;
    LPTSTR Filename;
    HANDLE hNew;
    UINT DupNumber;

    static SECURITY_ATTRIBUTES SecAttr = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };

    /* Some parameters used for read, write, and append, respectively */
    static struct REDIR_PARAMS
    {
        DWORD dwDesiredAccess;
        DWORD dwShareMode;
        DWORD dwCreationDisposition;
    } RedirParams[] =
    {
        {GENERIC_READ , FILE_SHARE_READ | FILE_SHARE_WRITE, OPEN_EXISTING}, // REDIR_READ
        {GENERIC_WRITE, FILE_SHARE_READ                   , CREATE_ALWAYS}, // REDIR_WRITE
        {GENERIC_WRITE, FILE_SHARE_READ                   , OPEN_ALWAYS  }  // REDIR_APPEND
    };

    for (Redir = RedirList; Redir; Redir = Redir->Next)
    {
        Filename = DoDelayedExpansion(Redir->Filename);
        if (!Filename)
            goto redir_error;
        StripQuotes(Filename);

        if (*Filename == _T('&'))
        {
            DupNumber = Filename[1] - _T('0');
            if (DupNumber >= 10 ||
                !DuplicateHandle(GetCurrentProcess(),
                                 GetHandle(DupNumber),
                                 GetCurrentProcess(),
                                 &hNew,
                                 0,
                                 TRUE,
                                 DUPLICATE_SAME_ACCESS))
            {
                hNew = INVALID_HANDLE_VALUE;
            }
        }
        else
        {
            hNew = CreateFile(Filename,
                              RedirParams[Redir->Mode].dwDesiredAccess,
                              RedirParams[Redir->Mode].dwShareMode,
                              &SecAttr,
                              RedirParams[Redir->Mode].dwCreationDisposition,
                              0,
                              NULL);
        }

        if (hNew == INVALID_HANDLE_VALUE)
        {
            /* TODO: Print a more detailed message */
            ConErrResPrintf(Redir->Mode == REDIR_READ ? STRING_CMD_ERROR1 : STRING_CMD_ERROR3,
                            Filename);
            cmd_free(Filename);
redir_error:
            /* Undo all the redirections before this one */
            UndoRedirection(RedirList, Redir);
            return FALSE;
        }

        if (Redir->Mode == REDIR_APPEND)
            SetFilePointer(hNew, 0, NULL, FILE_END);
        Redir->OldHandle = GetHandle(Redir->Number);
        SetHandle(Redir->Number, hNew);

        TRACE("%d redirected to: %s\n", Redir->Number, debugstr_aw(Filename));
        cmd_free(Filename);
    }
    return TRUE;
}

VOID
UndoRedirection(REDIRECTION *Redir, REDIRECTION *End)
{
    for (; Redir != End; Redir = Redir->Next)
    {
        CloseHandle(GetHandle(Redir->Number));
        SetHandle(Redir->Number, Redir->OldHandle);
        Redir->OldHandle = INVALID_HANDLE_VALUE;
    }
}

VOID
FreeRedirection(REDIRECTION *Redir)
{
    REDIRECTION *Next;
    for (; Redir; Redir = Next)
    {
        Next = Redir->Next;
        ASSERT(Redir->OldHandle == INVALID_HANDLE_VALUE);
        cmd_free(Redir);
    }
}

#endif /* FEATURE_REDIRECTION */
